@Inner 注解使用及原理
💡注解功能说明
@Inner 注解用于控制微服务接口的访问权限,帮助决定哪些接口需要登录才能访问、哪些接口可以公开访问、哪些接口只能在系统内部调用。
快速决策指南
选择合适的配置:
| 场景 | 配置方式 | 是否需要登录 | 能否外部访问 |
|---|
| 用户个人中心 | 不加注解 | ✅ 需要 | ✅ 可以 |
| 获取验证码 | @Inner(false) | ❌ 不需要 | ✅ 可以 |
| 定时任务调用 | @Inner | ❌ 不需要 | ❌ 不可以 |
三种使用场景
场景一:需要登录的接口(不加注解)
什么时候用:
- 用户登录后才能访问的功能
- 常规的增删改查操作
- 需要验证用户身份的接口
怎么做:
@GetMapping("/user/profile")
public User getUserProfile() {
// 不需要添加 @Inner 注解
// 前端请求会自动携带 token
return userService.getProfile();
}
工作流程:
- 前端发送请求,携带登录 token
- Gateway 网关验证 token 是否有效
- 验证通过后才能访问接口
场景二:公开接口(使用 @Inner(false))
什么时候用:
怎么做:
@Inner(false) // 重点:设置为 false
@GetMapping("/captcha")
public String getCaptcha() {
// 任何人都可以访问,不需要登录
return captchaService.generate();
}
特点:
- 不需要 token
- 可以直接通过网关访问
- 适合完全公开的接口
场景三:系统内部调用(使用 @Inner)
什么时候用:
- 定时任务需要调用的接口
- 消息队列触发的接口
- 服务之间的内部通信
怎么做:
@Inner // 重点:只写 @Inner,默认为 true
@PostMapping("/order/sync")
public void syncOrder() {
// 只能被系统内部调用
// 外部无法直接访问
orderService.sync();
}
特点:
- 只能被内部服务调用
- 外部无法直接访问
- 不需要用户 token
工作原理(了解即可)
注解定义
public @interface Inner {
/**
* true: 只能内部调用(默认)
* false: 可以外部访问
*/
boolean value() default true;
}
系统启动时做了什么
系统会自动扫描所有标记了 @Inner 的接口,并加入白名单:
// 系统启动时自动执行
public void afterPropertiesSet() {
// 扫描所有接口
// 找到带 @Inner 注解的接口
// 自动添加到忽略列表(白名单)
}
运行时如何检查
通过 AOP 切面进行权限验证:
@Around("@annotation(inner)")
public Object around(ProceedingJoinPoint point, Inner inner) {
String header = request.getHeader(SecurityConstants.FROM);
// 如果是内部调用模式,检查请求头
if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) {
throw new AccessDeniedException("禁止外部访问");
}
return point.proceed();
}
常见问题
问题 1:使用路径变量要小心
⚠路径变量使用注意
系统会将路径变量替换为通配符 *,可能导致意外的接口暴露。建议使用更具体的路径设计,在使用 SecurityUtils.getUser() 时,确保接口未被错误地加入忽略列表。
示例代码:
@Inner
@GetMapping("/user/{id}") // 注意:路径变量
public User getUser(@PathVariable Long id) {
return userService.getById(id);
}
问题说明:
- 系统会把
{id} 替换成通配符 *
- 变成
/user/* 都可以访问
- 可能导致接口意外暴露
解决方案:
// 方案 1: 使用请求参数代替路径变量
@Inner
@GetMapping("/user")
public User getUser(@RequestParam Long id) {
return userService.getById(id);
}
// 方案 2: 使用更具体的路径
@Inner
@GetMapping("/internal/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.getById(id);
}
问题 2:接口设置为公开,但携带错误 token 时被拦截
现象:
@Inner(false) // 设置为公开
@GetMapping("/public/data")
public String getData() {
return "public data";
}
访问时:
- 不带 token → ✅ 正常访问
- 带错误 token → ❌ 被拦截
原因:
Spring Security 默认会验证请求中的 token,即使接口是公开的。
解决方案:
如果需要完全忽略 token 验证,可以关闭 Spring Security 的这个行为:
